﻿using FRMS.DAL;
using FRMS.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Timer = System.Timers.Timer;

namespace FRMS.BUS.ServerPlatform
{
    public class SerialPortBUS
    {
        public static int CheckEnable = 0;
        public SerialPortBUS()
        {
            CheckEnable = Convert.ToInt32(AppSettingsHelper.Configuration["CheckEnable"]);
            OpenttyS0SerialPort("COM6", 9600, 2, 8, 1);
            QueryElevatorStatus();
        }
        public static SerialPort myS0SerialPort_1 = new SerialPort();
        public static List<string> cmdlist = new List<string>();
        public static string controllerLaststate = "";
        public static int workmodeLast = 0;
        public static int sendtime = 0;
        public void OpenttyS0SerialPort(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            try
            {
                SetS0SerialPortConfig(portName, baudRate, parity, dataBits, stopBits);
                myS0SerialPort_1.Open();
                LogHelper.Log(portName + "打开成功");

            }
            catch (Exception e)
            {
                LogHelper.Log("ttyS0打开异常" + e.ToString());
            }
        }

        /// <summary>
        /// 串口接收数据 位置
        /// </summary>
        private static int pSerialPortRecv_S0_1 = 0;
        /// <summary>
        /// 缓存区大小的长度    
        /// 缓冲区可调大
        /// （接收数据处理定时器 内接收数据量 小于下面设置的值即可）
        /// </summary>
        private static int byteLength = 40960;
        /// <summary>
        /// 串口 接收数据处理定时器
        /// </summary>
        private Timer SerialPortRecvTimerS0_1;

        /// <summary>
        /// 串口接收字节 缓存区大小
        /// </summary>
        public byte[] byteSerialPortRecvS0_1 = new byte[byteLength];

        /// <summary>
        /// 广播 收到的数据 事件
        /// </summary>
        public event EventHandler<SerialPort485_1RecvEventArgs> ReceivedDataEventS0_1;

        /// <summary>
        /// 数据处理定时器
        /// 定时检查缓冲区是否有数据，如果有数据则将数据处理并广播。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// 
        public class SerialPort485_1RecvEventArgs : EventArgs
        {
            /// <summary>
            /// 广播 收到的串口数据
            /// </summary>
            public readonly byte[] RecvData_485_1 = new byte[byteLength];

            /// <summary>
            /// 收到数据 的 长度
            /// </summary>
            public readonly int RecvDataLength_485_1;

            /// <summary>
            /// 将 收到的数据 转化成 待广播的数据
            /// </summary>
            public SerialPort485_1RecvEventArgs(byte[] recvData, int recvDataLength)
            {
                recvData.CopyTo(RecvData_485_1, 0);
                RecvDataLength_485_1 = recvDataLength;
            }
        }

        /// <summary>
        /// 数据处理定时器
        /// 定时检查缓冲区是否有数据，如果有数据则将数据处理并广播。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SPRecvTimer_Tick_ttyS0_1(object sender, EventArgs e)
        {
            byte[] TemporaryData = new byte[byteLength];
            int TemporaryDataLength = 0;

            if (ReceivedDataEventS0_1 != null)
            {
                byteSerialPortRecvS0_1.CopyTo(TemporaryData, 0);
                TemporaryDataLength = pSerialPortRecv_S0_1;

                ReceivedDataEventS0_1.Invoke(this, new SerialPort485_1RecvEventArgs(TemporaryData, TemporaryDataLength));
                // 数据处理完后，将指针指向数据头，等待接收新的数据
                pSerialPortRecv_S0_1 = 0;
            }
        }

        private void SetS0SerialPortConfig(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                portName = $"/dev/{portName}";
            }
            // 串口 参数设置
            myS0SerialPort_1.PortName = portName;
            myS0SerialPort_1.BaudRate = baudRate;
            switch (parity)
            {
                case 0:
                default:
                    myS0SerialPort_1.Parity = Parity.None;
                    break;
                case 1:
                    myS0SerialPort_1.Parity = Parity.Odd;
                    break;
                case 2:
                    myS0SerialPort_1.Parity = Parity.Even;
                    break;
                case 3:
                    myS0SerialPort_1.Parity = Parity.Mark;
                    break;
                case 4:
                    myS0SerialPort_1.Parity = Parity.Space;
                    break;
            }
            myS0SerialPort_1.DataBits = ((4 < dataBits) && (dataBits < 9)) ? dataBits : 8;
            switch (stopBits)
            {
                case 0:
                    myS0SerialPort_1.StopBits = StopBits.None;
                    break;
                case 1:
                default:
                    myS0SerialPort_1.StopBits = StopBits.One;
                    break;
                case 2:
                    myS0SerialPort_1.StopBits = StopBits.OnePointFive;
                    break;
                case 3:
                    myS0SerialPort_1.StopBits = StopBits.Two;
                    break;
            }
            //my485SerialPort_1.ReadTimeout = 200;
            myS0SerialPort_1.RtsEnable = true;
            myS0SerialPort_1.DataReceived += new SerialDataReceivedEventHandler(DataReceived_ttyS0);

            // 串口 接收数据处理定时器 参数设置
            SerialPortRecvTimerS0_1 = new System.Timers.Timer();
            SerialPortRecvTimerS0_1.Interval = 100;
            SerialPortRecvTimerS0_1.AutoReset = false;
            SerialPortRecvTimerS0_1.Elapsed += new ElapsedEventHandler(SPRecvTimer_Tick_ttyS0_1);
        }

        private void DataReceived_ttyS0(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                //SerialPortRecvTimerS0_1.Stop();

                byte[] ReadBuf = new byte[myS0SerialPort_1.BytesToRead];
                if (pSerialPortRecv_S0_1 > byteLength - ReadBuf.Length)
                {
                    pSerialPortRecv_S0_1 = 0;
                }
                myS0SerialPort_1.Read(ReadBuf, 0, ReadBuf.Length);
                ReadBuf.CopyTo(byteSerialPortRecvS0_1, pSerialPortRecv_S0_1);
                pSerialPortRecv_S0_1 += ReadBuf.Length;
                try
                {
                    for (int i = 0; i < byteSerialPortRecvS0_1.Length; i++)
                    {
                        if (byteSerialPortRecvS0_1[i + 1] == 0x03 && byteSerialPortRecvS0_1[i + 2] == 0x08)
                        {
                            byte[] buffer = new byte[11];
                            for (int j = 0; j < 11; j++)
                            {
                                buffer[j] = byteSerialPortRecvS0_1[i + j];
                            }
                            byte[] crc = Crc18(buffer);
                            if (crc[1] == byteSerialPortRecvS0_1[i + 11] && crc[0] == byteSerialPortRecvS0_1[i + 12])
                            {
                                Array.Clear(byteSerialPortRecvS0_1, i, buffer.Length + 2);
                                //1、心跳数据
                                DeviceInfo devinfo = new DeviceInfo();
                                DeviceDAL dal = new DeviceDAL();

                                string controllerstateByte = Convert.ToString(buffer[10], 2).PadLeft(8, '0');//40004-bit0-bit7
                                string workmodestateByte = Convert.ToString(buffer[9], 2).PadLeft(8, '0');// 40004-bit8-bit15
                                int ladderid = buffer[0];
                                List<DeviceInfo> list = dal.QueryDeviceInfByLadderid(ladderid);
                                if (list == null) return;
                                if (list.Count > 0)
                                {
                                    if (controllerstateByte.Substring(5, 1) == "1" || controllerstateByte.Substring(6, 1) == "1")//判断C-link或者CAN连接状态
                                    {
                                        //控制器在线
                                        foreach (var item in list)
                                        {
                                            if (item.ControllerState != "在线")
                                            {
                                                //更新控制器状态
                                                devinfo.ID = item.ID;
                                                devinfo.ControllerState = "在线";
                                                devinfo.ControllerStateUpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                                bool result = dal.UpdateN71SPControllerState(devinfo);
                                                //新增日志记录
                                                if (result == true)
                                                {
                                                    //新增日志记录
                                                    bool res = dal.AddControllerState(devinfo);
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        foreach (var item in list)
                                        {
                                            if (item.ControllerState != "离线")
                                            {
                                                //更新控制器状态
                                                devinfo.ID = item.ID;
                                                devinfo.ControllerState = "离线";
                                                devinfo.ControllerStateUpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                                bool result = dal.UpdateN71SPControllerState(devinfo);
                                                //新增日志记录
                                                if (result == true)
                                                {
                                                    //新增日志记录
                                                    bool res = dal.AddControllerState(devinfo);
                                                }
                                            }
                                        }
                                    }

                                    int workmode = int.Parse(controllerstateByte.Substring(4, 1));//当前工作模式
                                    if (workmode == 1)
                                    {
                                        foreach (var item in list)
                                        {
                                            if (item.Workmode != 1)
                                            {
                                                devinfo.ID = item.ID;
                                                devinfo.Workmode = workmode;
                                                devinfo.WorkmodeUpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                                bool result = dal.UpdateN71SPWorkmode(devinfo);
                                                //新增日志记录
                                                if (result == true)
                                                {
                                                    //新增日志记录
                                                    bool res = dal.Addworkmode(devinfo);
                                                }
                                            }

                                        }
                                    }
                                    else
                                    {
                                        foreach (var item in list)
                                        {
                                            if (item.Workmode != 0)
                                            {

                                                devinfo.ID = item.ID;
                                                devinfo.Workmode = workmode;
                                                devinfo.WorkmodeUpdateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                                bool result = dal.UpdateN71SPWorkmode(devinfo);
                                                //新增日志记录
                                                if (result == true)
                                                {
                                                    //新增日志记录
                                                    bool res = dal.Addworkmode(devinfo);
                                                }
                                            }
                                        }
                                    }

                                    //获取设置的梯控模式与实际设备端是否一致，不一致则下发梯控命令
                                    ElevatorDAL ElevatorDAL = new ElevatorDAL();
                                    ElevatorInfo elevatorInfo = ElevatorDAL.QueryElevatorByLadderID(ladderid);
                                    if (elevatorInfo != null)
                                    {
                                        if (elevatorInfo.Workmode != workmode)
                                        {
                                            ModbusRtu modbusRtu = new ModbusRtu();
                                            string cmd = modbusRtu.GetTKCmd(ladderid, elevatorInfo.Workmode);
                                            CmdAdd(cmd);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception ex1)
                {
                    LogHelper.Log(ex1.ToString());
                }

                //SerialPortRecvTimerS0_1.Start();
            }
            catch (Exception)
            {
            }

        }

        #region CRC校验
        public byte[] Crc18(byte[] buffer, int start = 0, int len = 0)
        {
            if (buffer == null || buffer.Length == 0) return null;
            if (start < 0) return null;
            if (len == 0) len = buffer.Length - start;
            int length = start + len;
            if (length > buffer.Length) return null;
            ushort crc = 0xFFFF;// Initial value
            for (int i = start; i < length; i++)
            {
                crc ^= buffer[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 1) > 0)
                        crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005
                    else
                        crc = (ushort)(crc >> 1);
                }
            }
            byte[] ret = BitConverter.GetBytes(crc);
            Array.Reverse(ret);
            return ret;
        }
        #endregion

        public static bool SendMsg(string data)
        {
            try
            {
                myS0SerialPort_1.Write(ToBytesSendDate(data), 0, ToBytesSendDate(data).Length);
                return true;
            }
            catch (Exception e)
            {

                LogHelper.Log(data + "命令发送失败");
                return false;
            }
        }

        public static bool CmdAdd(string data)
        {
            try
            {
                cmdlist.Add(data);
                return true;
            }
            catch (Exception e)
            {

                LogHelper.Log(data + "命令发送失败");
                return false;
            }
        }

        public static byte[] ToBytesSendDate(string str)
        {
            List<byte> result = new List<byte>();
            if (str.Contains(" "))
            {
                string[] strs = str.Trim().Split(' ');
                foreach (string s in strs)
                {
                    result.Add(Convert.ToByte(s.Trim(), 16));
                }
            }
            else
            {
                result.Add(Convert.ToByte(str, 16));
            }
            return result.ToArray();
        }


        public static CancellationTokenSource tokenSource = new CancellationTokenSource();
        public CancellationToken token = tokenSource.Token;
        public ManualResetEvent resetEvent = new ManualResetEvent(true);
        public void QueryElevatorStatus()
        {
            try
            {
                Task task = new Task(async () =>
                {
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }
                    resetEvent.WaitOne();
                    //do something
                    DateTime startTime = DateTime.Now;
                    while (true)
                    {
                        try
                        {
                            TimeSpan secondSpan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                            if (secondSpan.TotalMilliseconds > 1000)
                            {
                                startTime = DateTime.Now;
                                foreach (var item in DataDownLoad.elevator_dic)
                                {
                                    if (cmdlist.Count > 0)
                                    {
                                        for (int i = 0; i < cmdlist.Count; i++)
                                        {
                                            SendMsg(cmdlist[0]);
                                            cmdlist.RemoveAt(0);
                                            Wait(1000);
                                        }
                                    }
                                    else
                                    {
                                        SendMsg(item.Value);
                                        Wait(1000);
                                    }
                                }
                            }
                        }
                        catch
                        {

                        }
                    }
                }, token);
                task.Start();
            }
            catch (Exception e)
            {
            }
        }

        public bool Wait(int second)
        {
            try
            {
                DateTime startTime = DateTime.Now;
                while (true)
                {
                    TimeSpan secondSpan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                    if (secondSpan.TotalMilliseconds > second)
                    {
                        break;
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}
